﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace MvcRazorConverter
{
    public static class RazorConversor
    {
        public static void ConvertAll(string directory, string contentPlaceHolderID)
        {
            string[] array = Directory.GetFiles(directory, "*.aspx", SearchOption.AllDirectories).Concat(
                             Directory.GetFiles(directory, "*.ascx", SearchOption.AllDirectories)).Concat(
                             Directory.GetFiles(directory, "*.master", SearchOption.AllDirectories)).ToArray();

            foreach (var fileName in array)
            {
                string aspxCode = File.ReadAllText(fileName);
                string razorCode = ConvertToRazor(aspxCode, contentPlaceHolderID);
                File.Copy(fileName, fileName.ToLower().Replace("aspx", "cshtml").Replace("ascx", "cshtml").Replace("master", "cshtml"), true);
                File.WriteAllText(fileName.ToLower().Replace("aspx", "cshtml").Replace("ascx", "cshtml").Replace("master", "cshtml"), razorCode); //rename manually to update .csproj & source control 
                Console.WriteLine(fileName.ToString() + " has been properly converted");
            }
        }
        static readonly string[] DefaultNamespaces = new string[] 
        { 
            "System.Web.Helpers",  
            "System.Web.Mvc", 
            "System.Web.Mvc.Ajax", 
            "System.Web.Mvc.Html", 
            "System.Web.Routing", 
            "System.Web.WebPages", 
        };

        public static string ConvertToRazor(string aspxCode, string contentPlaceHolderID)
        {
            return ConvertToRazor(aspxCode, DefaultNamespaces, contentPlaceHolderID);
        }

        public static string ConvertToRazor(string aspxCode, string[] defaultNamespaces, string contentPlaceHolderID)
        {
            //namespaces 
            string text2 = Regex.Replace(aspxCode, @"<%@\s+Import Namespace=""(?<ns>.*?)""\s+%>",
                m => defaultNamespaces.Contains(m.Groups["ns"].Value) ? null : "@using " + m.Groups["ns"].Value);

            //headers 
            string text3 = Regex.Replace(text2, @"<%@\s(?<dir>.*?)%>", m => "@{ " + m.Groups["dir"].Value + "}"); // Preserves headers 

            //comments  
            string text3_1 = Regex.Replace(text3, @"<%--(?<expr>.*?)--%>", m =>
            {
                string expr = m.Groups["expr"].Value.Trim();
                string cleanExpr = Regex.Replace(expr, @"(""(\\""|[^""])*"")|(@""([^""]|"""")*"")|(\([^\(\)]*(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*\))", m2 => "");
                return "@*" + expr + "*@";
            }, RegexOptions.Singleline);

            //expressions  
            string text4 = Regex.Replace(text3_1, @"<%[=:](?<expr>.*?)%>", m =>
            {
                string expr = m.Groups["expr"].Value.Trim();
                string cleanExpr = Regex.Replace(expr, @"(""(\\""|[^""])*"")|(@""([^""]|"""")*"")|(\([^\(\)]*(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*\))", m2 => "");
                return "@(" + expr + ")"; //Always between brackets
            }, RegexOptions.Singleline);

            //sections  
            string text4_1 = Regex.Replace(text4, @"<asp:Content(.*?) ContentPlaceHolderID=""(?<name>.*?)"" (.*?)>(?<content>.*?)</asp:Content>", m =>
            {
                string name = m.Groups["name"].Value.Trim();

                string expr = m.Groups["content"].Value.Trim();
                string cleanExpr = Regex.Replace(expr, @"(""(\\""|[^""])*"")|(@""([^""]|"""")*"")|(\([^\(\)]*(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*\))", m2 => "");

                //Do not put main content in a section
                if (name == contentPlaceHolderID)
                {
                    return expr;
                }
                else if (name == "TitleContent") //TitleContent now goes into the ViewBag
                {
                    return "@{ \r\n ViewBag.Title = \"" + expr + "\"; \r\n}";
                }
                else
                {
                    //Normal sections
                    return "@section " + name + " { \r\n" + expr + " \r\n}";
                }

            }, RegexOptions.Singleline);

            //ContentPlaceholders inside a master page  
            string text5 = Regex.Replace(text4_1, @"<asp:ContentPlaceHolder(.*?) ID=""(?<name>.*?)"" (.*?)/>", m =>
            {
                return ConvertContentPlaceHolder(contentPlaceHolderID, m);

            }, RegexOptions.Singleline);

            string text5_1 = Regex.Replace(text5, @"<asp:ContentPlaceHolder(.*?) ID=""(?<name>.*?)"" (.*?)></asp:ContentPlaceHolder>", m =>
            {
                return ConvertContentPlaceHolder(contentPlaceHolderID, m);

            }, RegexOptions.Singleline);


            //code blocks 
            string finalText = Regex.Replace(text5_1, @"<%(?<code>.*?)%>", m =>
            {
                string code = m.Groups["code"].Value.Trim();

                Dictionary<string, string> stringLiterals = new Dictionary<string, string>();

                code = Regex.Replace(code, @"(""(\\""|[^""])*"")|(@""([^""]|"""")*"")", m2 =>
                {
                    string key = "<$" + stringLiterals.Count + "$>";
                    stringLiterals.Add(key, m2.Value);
                    return key;
                });

                string result = Regex.Replace(code,
                    @"((?<blockHeader>(else|(for|switch|foreach|using|while|if)\s*\([^\(\)]*(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*\))\s*)" +
                    @"((?<fullBlock>{[^{}]*(((?'OpenCurly'{)[^{}]*)+((?'CloseCurly-OpenCurly'})[^{}]*)+)*})|(?<openblock>{.*))|" +
                    @"(?<text>((?!({|}|\s)(for|switch|foreach|using|while|if|else if|else)(\s|{|\()).)+))",
                    m2 =>
                    {
                        if (m2.Value.Trim().Length == 0 || m2.Value.StartsWith("else") || m2.Value.StartsWith("}"))
                            return m2.Value;

                        if (m2.Groups["text"].Success)
                            return "@{ " + m2.Value.Trim() + "}\r\n";

                        return "@" + m2.Value;
                    }, RegexOptions.ExplicitCapture | RegexOptions.Singleline);

                result = Regex.Replace(result, @"<\$\d+\$>",
                    m2 => stringLiterals[m2.Value]);

                //correct else @if TO else if
                result = Regex.Replace(result, @"else @if",
                      m2 => "else if");

                return result;
            }, RegexOptions.Singleline);

            string topline = finalText.Substring(0, finalText.IndexOf('}') + 1);
            if (topline.IndexOf('<') != -1)
            {

                int startIndex = topline.IndexOf('<') + 1;
                topline = topline.Substring(startIndex);
                int endIndex = topline.LastIndexOf('>');
                topline = topline.Substring(0, endIndex);
                topline = "@model " + topline;
            }
            else
            {
                topline = string.Empty;
            }
            string finalResult = topline + finalText.Substring(finalText.IndexOf('}') + 1);

            return finalResult;
        }

        private static string ConvertContentPlaceHolder(string contentPlaceHolderID, Match m)
        {
            string name = m.Groups["name"].Value.Trim();


            //MainContent? Make a Razor call to RenderBody()
            if (name == contentPlaceHolderID)
            {
                return "@RenderBody()";
            }
            else if (name == "TitleContent") //TitleContent now has to show Title from ViewBag
            {
                return "@ViewBag.Title";
            }
            else
            {
                //Normal sections
                return "@RenderSection(\"" + name + "\", required: false)";
            }
        }
    }

}
